package com.example.maxboxtwo.components.activities;

import android.Manifest;
import android.animation.ValueAnimator;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.animation.AccelerateDecelerateInterpolator;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.example.maxboxtwo.R;
import com.example.maxboxtwo.components.function.trumpet.TrumpetView;

import java.io.IOException;
import java.util.List;

import pub.devrel.easypermissions.AppSettingsDialog;
import pub.devrel.easypermissions.EasyPermissions;

public class DecibelDevice extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
    private static final int REQUEST_RECORD_AUDIO_CODE = 1;
    private static final int MAX_LENGTH=100000;
    private int BASE = 1;
    private double db = 0.0;
    private int minDb = 0;
    private int MaxDb = 0;
    private double lastDb = 0.0;

    private Handler handler = new Handler();
    private ValueAnimator valueAnimator;
    private MediaRecorder mediaRecorder;
    private String filePath;
    private TrumpetView decibelView;



    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.decibel_device);
        this.filePath  = "/dev/null";
        decibelView = findViewById(R.id.decibel);
    }



    @Override
    protected void onStart() {
        super.onStart();
        /**
         * check permission and record voice
         */
        checkPermissions();

    }

    @Override
    protected void onStop() {
        super.onStop();
        /**
         * stop record
         */
        stopRecord();
    }

    @Override
    protected void onDestroy() {
        this.handler.removeCallbacks(this.update);
        // cancel animator in case store reveal
        cancelAnimator();
        super.onDestroy();
    }


    @Override
    protected void onResume() {
        super.onResume();
    }


    Runnable update = new Runnable() {
        @Override
        public void run() {
            DecibelDevice.this.updateMicStatus();
        }
    };

    // update decibel of runnable
    private void checkPermissions(){
     if(!EasyPermissions.hasPermissions(this, Manifest.permission.RECORD_AUDIO)){
            EasyPermissions.requestPermissions(this,"open you microphone please ,we need it to listen volume of voice",REQUEST_RECORD_AUDIO_CODE,Manifest.permission.RECORD_AUDIO);
     }else{
         startRecord();
     }
    }

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        startRecord();
    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        if(EasyPermissions.somePermissionPermanentlyDenied(this,perms)){
            new AppSettingsDialog.Builder(this).setTitle("request microphone").setRationale("we need you microphone to record voice,or don't work").build().show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //
        EasyPermissions.onRequestPermissionsResult(requestCode,permissions,grantResults,this);
    }

    public void startRecord(){
        if(mediaRecorder==null){
            mediaRecorder = new MediaRecorder();
        }

        try {
            this.mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            this.mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
            this.mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            this.mediaRecorder.setOutputFile(this.filePath);

            this.mediaRecorder.setMaxDuration(MAX_LENGTH);
            this.mediaRecorder.prepare();
            this.mediaRecorder.start();

            updateMicStatus();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void updateMicStatus(){
    if(this.mediaRecorder!=null){
        double maxAmplitude = getMaxAmplitude()/this.BASE;
        if(maxAmplitude>1.0d){
            // translate value of amplitude to value of decibel
            db = Math.log10(maxAmplitude)*20.0d;
            if(this.minDb ==0){
                // minDb is minimum volume of voice
                this.minDb = (int)db;
            }
            decibelView.setVoiceVolume((int)db);
            decibelView.postInvalidate();
//            startAnimator();
//            this.lastDb = db;
        }
        // in this method, update the value of decibel per 500s
        this.handler.postDelayed(this.update,800);
      }
    }

    // get max amplitude
    private double getMaxAmplitude(){
        if(this.mediaRecorder==null){
            return 5.0;
        }
        try {
            return this.mediaRecorder.getMaxAmplitude();
        } catch (IllegalStateException e) {
            return 0.0;
        }
    }

    // cancel the valueAnimator
    private void cancelAnimator(){
        if(valueAnimator!=null&& valueAnimator.isRunning()){
            valueAnimator.cancel();
        }
    }

    // start animator to update decibel
  /*  private void startAnimator(){
        cancelAnimator();
        valueAnimator = valueAnimator.ofFloat(new float[]{((float)this.lastDb),(float)(this.db)});
        valueAnimator.setDuration(400);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addUpdateListener((animation)-> {
                float floatValue = ((Float)animation.getAnimatedValue()).floatValue();
                // set value of decibel
                decibelView.setVoiceVolume((int)floatValue);

                int intValue = (int)(floatValue);
                // update to maxValue at moment
            if(intValue<180&&intValue>DecibelDevice.this.MaxDb){
                DecibelDevice.this.MaxDb = intValue;
                /*******i don't know what to do using this value**********/
            /*}
            if(intValue<DecibelDevice.this.minDb){
                DecibelDevice.this.minDb = intValue;
                /**********i don't know what to do using this value*****************/
          /*  }
        });
        valueAnimator.start();
    }*/

    // stop record
    private void stopRecord(){
    if(this.mediaRecorder!=null){
        try {
            this.mediaRecorder.stop();
            this.mediaRecorder.reset();
            this.mediaRecorder.release();
            this.mediaRecorder = null;
        } catch (IllegalStateException e) {
            this.mediaRecorder = null;
            e.printStackTrace();
        }
      }
    }


}
